// co
// all/race/allsettle/finnaly
// queue/promisequeue
// requestCache/请求重试
// layzman/flow
// promisify
// promiseLike
// 模拟微任务


function co(gen) {
    let g = gen()

    const next = (params) => {
        const { value, done } = g.next(params);
        if (done) return value;
        if (value.then) {
            return value.then(res => next(res))
        } else {
            return next(value)
        }
    }
    next()
}
// co(function* () {
//     let a = yield Promise.resolve(1);
//     let b = yield Promise.resolve(2);
//     console.log(a + b)
// })


Promise.myAll = function (promises) {
    let res = [];
    let count = 0;
    return new Promise((resolve, reject) => {
        for (let i = 0; i < promises.length; i++) {
            Promise.resolve(promises[i]).then(item => {
                res[i] = item
                count++
                if (count === promises.length) {
                    resolve(res)
                }
            }).catch(err => {
                reject(err)
            })
        }
    })
}
// Promise.myAll([
//     Promise.resolve(1),
//     Promise.resolve(2)
// ]).then((res) => console.log(res))

Promise.myAllSettled = function (promises) {
    let arr = promises.map(p => {
        return Promise.resolve(p)
            .then(val => ({
                type: 'resolved',
                val: val
            }))
            .catch(err => ({
                type: 'rejected',
                val: err
            }))
    })
    return Promise.all(arr)
}
// Promise.myAllSettled([
//     Promise.resolve(1),
//     0,
//     Promise.reject(new Error('1'))
// ]).then(res => console.log(res))



Promise.prototype.myFinally = function (callback) {
    return this
        .then(item => {
            callback(item)
            return item
        })
        .catch(err => {
            callback(err)
            throw err
        })
}
// Promise.resolve(1).then(res => res).myFinally(res => console.log('----', res))



class Queue {
    constructor() {
        this.list = [];
    }
    add(fn) {
        this.list.push(fn)
        return this;
    }
    async task() {
        for (let i = 0; i < this.list.length; i++) {
            await this.list[i]()
        }
    }
}
// const queue = new Queue();
// var delay = (timeout) => new Promise((resolve) => setTimeout(resolve, timeout))
// queue.add(() => delay(1000).then(() => console.log('task 1')))
//     .add(() => delay(2000).then(() => console.log('task 2')))
//     .task();


class PromiseQueue {
    constructor() {
        this.list = [];
        this.max = 2;
        this.count = 0;
    }
    add(fn) {
        this.list.push(fn)
        this.next()
        return this;
    }
    next() {
        if (this.count >= this.max) return;
        if (!this.list.length) return;
        let fn = this.list.shift();
        let p = fn();
        this.count++;
        p.then(this.done.bind(this)).catch(this.done.bind(this))
    }
    done() {
        this.count--;
        this.next()
    }
}
// const queue = new PromiseQueue();
// var delay = (timeout) => new Promise((resolve) => setTimeout(resolve, timeout))
// queue.add(() => delay(1000).then(() => console.log('task 1')))
//     .add(() => delay(2000).then(() => console.log('task 2')))

// https://blog.csdn.net/m0_51123132/article/details/125261026
class LazyMan {
    constructor() {
        this.taskList = []
        setTimeout(() => {
            this.next()
        }, 0)
    }
    log(msg) {
        let task = () => setTimeout(() => {
            console.log(msg)
            this.next()
        }, 0)
        this.taskList.push(task)
        return this;
    }
    sleep(time) {
        let task = () => setTimeout(() => {
            this.next()
        }, time)
        this.taskList.push(task)
        return this;
    }
    next() {
        let task = this.taskList.shift();
        task && task()
    }

}

// new LazyMan().log('1111').sleep(5000).log('22222')


function flow(promises) {
    let next = (index) => {
        if (!promises[index]) return;
        let res = promises[index]()
        if (res && typeof res.then === 'function') {
            res.then(() => next(index + 1))
        } else {
            next(index + 1)
        }
    }
    next(0)
}

const delay = (time) => new Promise(resolve => setTimeout(resolve, time))
flow([
    () => console.log('1'),
    () => console.log('2'),
    () => delay(1000).then(() => console.log('3')),
    () => delay(4000).then(() => console.log('4')),
    () => console.log('5'),
    () => console.log('6'),
])





function promisify(fn){
    return function(...args){
        return new Promise((resolve, reject) => {
            function callback(err, ...result){
                if(err) return reject(err)
                return resolve(result.length > 1 ? result : result[0])
            }
            fn.call(this, args, callback)
        })
    }
}
// 使用示例
// const fs = require('fs');
// const readFileAsync = promisify(fs.readFile);
// readFileAsync('2023/原理手写4/test/test.txt', { encoding: 'utf8' })
//     .then(contents => {
//         console.log(contents, 'neiron');
//     })
//     .catch(error => {
//         console.error('An error occurred:', error);
//     });


function request(url, max){
    return fetch(url).catch(err => max > 0 ? request(url, max -1) : resolve.reject(err))
}




let count = 0;
let max = 2;
let list = [];
let map = new Map();
function cacheRequest(url, scall, ecall){
    if(map.has(url)){
        return scall(map.get(url))
    }
    list.push({url,scall,ecall})
    processQueue()


}
async function processQueue(){
    if(count > max) return;
    if(!list.length) return;
    let res = list.shift();
    const {url,scall,ecall} = res;
    count++
    try {
        let res = await fetch(url)
        let data = await res.json();
        map.set(url, data)
        scall(url, data)
    } catch (error) {
        ecall(error)
    } finally {
        count--
        processQueue()
    }
}